package com.hup.vd.view;

import com.hup.utils.commons.TextUtil;
import com.hup.utils.commons.start.DialogHelper;
import com.hup.utils.commons.start.LookThemeUtil;
import com.hup.utils.commons.start.SimpleExitHandler;
import com.hup.utils.commons.start.SpringContext;
import com.hup.utils.commons.start.StartupInfo;
import com.hup.utils.commons.start.VersionConfig;
import com.hup.utils.swing.ComponentUtil;
import com.hup.utils.swing.customView.HListView;
import com.hup.utils.swing.frame.BaseFrame;
import com.hup.utils.swing.frame.WindowConfigBinder;
import com.hup.utils.swing.frame.WindowConfigBinder.FrameStateHandler;
import com.hup.utils.swing.model.BaseWindowConfig;
import com.hup.utils.swing.model.ButtonGroupMulti;
import com.hup.utils.swing.model.ButtonModelMulti;
import com.hup.vd.constant.TaskConstant.SortType;
import com.hup.vd.constant.TaskConstant.TaskState;
import com.hup.vd.model.bo.TaskListData;
import com.hup.vd.model.config.AppConfig;
import com.hup.vd.model.config.TaskInfo;
import com.hup.vd.service.FileDownloadService;
import com.hup.vd.service.TaskService;
import com.hup.vd.view.customView.TaskItemView;
import com.hup.vd.view.customView.TaskItemView.ItemHandler;
import com.hup.vd.view.taskEditor.TaskEditorDialog;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.util.StringUtils;

import javax.swing.ButtonGroup;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.KeyStroke;
import javax.swing.MenuElement;
import javax.swing.SwingUtilities;
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.net.URI;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Objects;

/**
 * @author hugan
 * @date 2022/1/11
 */
@Slf4j
public class MainFrame extends BaseFrame {

    private final TaskService taskService;
    private final AppConfig config;
    @Getter
    private boolean inited;

    private MainForm form;
    private TaskItemHandler taskItemHandler;
    private ButtonGroupMulti buttonGroup = new ButtonGroupMulti();

    public MainFrame() {
        super(StartupInfo.getApplicationChName());
        setSubTitle(StartupInfo.getVersion());
        SpringContext.executeIfExist(VersionConfig.class, versionConfig -> {
            if (!TextUtil.isBlank(versionConfig.env)) {
                setSubTitle(versionConfig.version + "-" + versionConfig.env);
            }
        });
        taskService = SpringContext.getBean(TaskService.class);
        config = SpringContext.getBean(AppConfig.class);

        initView();
        initListener();

        doInBackground("加载任务", () -> {
            form.labDownloading.setText("下载中(加载中...)");
            refreshTaskList();
        });
        inited = true;
    }

    private void initView() {
        form = new MainForm();
        setContentPane(form.root);
        LookThemeUtil.initFrameSize(this, 880, 600);
        setLocationRelativeTo(null);
        config.getWindowConfig().initFrame(this);
        form.btnOnTop.setSelected(this.isAlwaysOnTop());

        taskItemHandler = new TaskItemHandler();
        form.tabbedPane.setSelectedIndex(1);
        form.pCreateTask.txtUrl.setText(config.getLastUrl());
        form.pCreateTask.txtFilePath.setText(config.getLastSavePath());
        //设置
        form.pCreateTask.pSetting.initConfig(config);
        form.initRateLimit(config.getDownloadConfig().getDownloadRateLimit());
    }

    private void initListener() {
        WindowConfigBinder.bind(this, new FrameStateHandler() {
            @Override
            public BaseWindowConfig getConfig() {
                return config.getWindowConfig();
            }

            @Override
            public void saveConfig(String desc) {
                config.save();
            }
        });
        form.btnOnTop.addActionListener(e -> setAlwaysOnTop(form.btnOnTop.isSelected()));
        form.btnTray.addActionListener(e -> {
            //refreshTaskList(true);//用于调试界面
            //refreshTaskList(false);
            this.setVisible(false);
        });
        form.btnRefresh.setActionListener(e -> taskItemHandler.menuAction(TaskItemHandler.REFRESH));
        form.initRateLimitListener((kb, isAdjusting) -> {
            if (kb == null) {
                form.txtRateLimit.setText(" 限速:无 ");
            } else {
                form.txtRateLimit.setText(" 限速:" + FileUtils.byteCountToDisplaySize(kb * 1024) + "/s");
            }
            if (!isAdjusting) {
                config.getDownloadConfig().setDownloadRateLimit(kb);
                config.save();
                SpringContext.getBean(FileDownloadService.class).notifyConfigUpdated();
            }
        });
        form.pCreateTask.setViewHandler(req -> execute("创建任务", () -> {
            form.pCreateTask.btnCreate.setEnabled(false);
            form.pCreateTask.btnCreateNStart.setEnabled(false);
            config.setLastUrl(req.getUrl());
            config.setLastSavePath(req.getFilePath());
            config.save();

            return taskService.createTask(req);
        }, task -> {
            //更新ui
            refreshTaskList(true);
            scrollToDownloadTask(task);

            form.pCreateTask.btnCreate.setEnabled(true);
            form.pCreateTask.btnCreateNStart.setEnabled(true);
        }, (desc, e) -> {
            if (SpringContext.getBean(SimpleExitHandler.class).isExiting()) {
                log.info("e={}", e.getMessage());
                return;
            }
            this.onExecuteException(desc, e);

            form.pCreateTask.btnCreate.setEnabled(true);
            form.pCreateTask.btnCreateNStart.setEnabled(true);
        }));
        taskService.getTaskList().getListeners().add(e -> refreshTaskList());
        form.pTaskInfo.btnEdit.addActionListener(e ->
                doInBackground("编辑任务", () -> TaskEditorDialog.doShow(form.pTaskInfo.getTask())));
        form.pTaskInfo.labUrl.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                doInBackground("打开网址", () -> {
                    TaskInfo task = form.pTaskInfo.getTask();
                    if (task == null) return;
                    String url = StringUtils.hasText(task.getHtmlUrl()) ? task.getHtmlUrl() : task.getUrl();
                    Desktop.getDesktop().browse(new URI(url));
                });
            }
        });

        //CTRL+A 列表全选功能
        JComponent listViewParent = (JComponent) form.lvDownloading.getParent();
        listViewParent.registerKeyboardAction(e -> {
            buttonGroup.setSelection(null);//要清空,避免切换列表后,有旧item
            HListView<TaskItemView> listView = form.lvDownloading.isVisible() ? form.lvDownloading : form.lvFinished;
            for (int i = 0; i < listView.getChildCount(); i++) {
                TaskItemView view = listView.getChild(i);
                buttonGroup.addSelection(view.getModel());
            }
        }, KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW);
        listViewParent.registerKeyboardAction(e -> buttonGroup.setSelection(null),
                KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
    }

    private void scrollToDownloadTask(TaskInfo task) {
        for (int i = form.lvDownloading.getChildCount() - 1; i >= 0; i--) {
            TaskItemView view = form.lvDownloading.getChild(i);
            if (task.equals(view.getTask())) {
                ComponentUtil.scrollPaneTo(form.lvDownloading, view);
                return;
            }
        }
        log.info("找不到task?");
    }

    /**
     * 刷新任务
     * 重新加载任务文件,刷新ui
     */
    public void refreshTaskData() {
        taskItemHandler.menuAction(TaskItemHandler.REFRESH);
    }

    /**
     * 刷新界面列表
     * 只是刷新view, 不会重新读取task文件
     */
    private void refreshTaskList() {
        refreshTaskList(true);
        refreshTaskList(false);
    }

    private void refreshTaskList(boolean isDownload) {
        TaskListData taskList = taskService.getTaskList();
        log.info("refreshTaskList: isDownload={}, {}, ", isDownload, taskList);

        //此处,每次被通知后,会移除全部view,比较低效,不深究优化
        ArrayList<TaskInfo> tasks = isDownload ? taskList.getDownloading() : taskList.getFinished();
        ArrayList<TaskItemView> views = new ArrayList<>(tasks.size());
        for (TaskInfo task : tasks) views.add(new TaskItemView(task, taskItemHandler, buttonGroup));
        if (isDownload) {
            form.lvDownloading.setChildren(views);
            form.updateTaskCount(form.lvDownloading);
        } else {
            form.lvFinished.setChildren(views);
            form.updateTaskCount(form.lvFinished);
        }
    }

    /**
     * 更新TaskInfoPanel的信息
     * @param eventTask 触发调用的task
     * @param updateAll true-更新全部字段, false-更新图像
     */
    public void updateTaskInfoPanel(TaskInfo eventTask, boolean isDelete, boolean updateAll) {
        //即使页签没选中, 也要更新
        if (!Objects.equals(form.pTaskInfo.getTask(), eventTask)) {
            //log.info("非当前task, eventTask={}", eventTask.getUid());//test
            return;
        }
        if (isDelete) {
            form.pTaskInfo.setTask(null);
        } else {
            if (updateAll) {
                form.pTaskInfo.setTask(eventTask);
            } else {
                form.pTaskInfo.updateProgressImage();
            }
        }
    }

    @Override
    public boolean onWindowClosing(WindowEvent e) {
        SpringContext.getBean(SimpleExitHandler.class).doExit();
        return false;
    }

    public void afterCreateTasks(ArrayList<TaskInfo> taskInfos) {
        SwingUtilities.invokeLater(() -> {
            refreshTaskList(true);
            scrollToDownloadTask(taskInfos.get(0));
            form.pCreateTask.txtUrl.setText(config.getLastUrl());
            form.pCreateTask.txtFilePath.setText(config.getLastSavePath());
        });
    }

    /**
     * @param netSpeed 网速,单位KB,nullable
     */
    public void updateNetSpeed(Integer netSpeed) {
        String text = netSpeed == null ? "" : FileUtils.byteCountToDisplaySize(netSpeed * 1024) + "/s";
        form.txtCurrentRate.setText(text);
    }

    private class TaskItemHandler implements ItemHandler {
        private static final String START = "开始";
        private static final String PAUSE = "暂停";
        private static final String ALL_START = "全部开始";
        private static final String ALL_PAUSE = "全部暂停";
        private static final String SORT = "排序";
        private static final String REFRESH = "刷新列表";
        private static final String RESET = "重新下载";
        private static final String DEL_TASK = "删除任务";
        private static final String DEL_TASK_N_FILE = "删除任务和文件";

        private final JPopupMenu menu;
        private final JMenuItem menuStart, menuPause, menuAllStart, menuAllPause;
        private final HashMap<String, JRadioButtonMenuItem> sortMenus;
        /**
         * 上次打开菜单时,的任务是否是[已完成]
         */
        private boolean lastMenuDownloadTask;

        public TaskItemHandler() {
            menu = new JPopupMenu();
            menu.addSeparator();
            menu.add(menuStart = new JMenuItem(START));
            menu.add(menuPause = new JMenuItem(PAUSE));
            menu.addSeparator();
            menu.add(menuAllStart = new JMenuItem(ALL_START));
            menu.add(menuAllPause = new JMenuItem(ALL_PAUSE));
            menu.addSeparator();
            //排序
            JMenu sortMenu = new JMenu(SORT);
            menu.add(sortMenu);
            ActionListener sortListener = e -> doInBackground("排序", () -> menuSortAction(e));
            ButtonGroup buttonGroup = new ButtonGroup();
            sortMenus = new HashMap<>();
            for (SortType sortType : SortType.values()) {
                JRadioButtonMenuItem item = new JRadioButtonMenuItem(sortType.name);
                sortMenus.put(sortType.name, item);
                buttonGroup.add(item);
                sortMenu.add(item);
                item.addActionListener(sortListener);
            }
            menu.add(new JMenuItem(REFRESH));
            menu.addSeparator();
            menu.add(new JMenuItem(RESET));
            menu.add(new JMenuItem(DEL_TASK));
            menu.add(new JMenuItem(DEL_TASK_N_FILE));
            menu.addSeparator();

            ActionListener listener = e -> menuAction(e.getActionCommand());
            for (MenuElement item : menu.getSubElements()) {
                ((JMenuItem) item.getComponent()).addActionListener(listener);
            }
        }

        @Override
        public void onClickItem(TaskInfo task) {
            form.pTaskInfo.setTask(task);
            form.tabbedPane.setSelectedIndex(0);
        }

        @Override
        public void showMenu(MouseEvent e, TaskItemView view) {
            boolean finished = view.getTask().getFinishedState();
            lastMenuDownloadTask = !finished;
            menuStart.setEnabled(!finished);
            menuPause.setEnabled(!finished);
            menuAllStart.setEnabled(!finished);
            menuAllPause.setEnabled(!finished);
            sortMenus.get(lastMenuDownloadTask ? config.getDownloadSortType() : config.getFinishSortType())
                    .setSelected(true);
            menu.show(e.getComponent(), e.getX(), e.getY());
        }

        @Override
        public void switchTaskState(TaskInfo task, boolean isStart) {
            doInBackground("操作", () -> taskService.switchTaskState(task, isStart));
        }

        /**
         * @return 选中的任务列表, 按ui列表排序
         */
        private ArrayList<TaskInfo> getSelectedTask() {
            ArrayList<ButtonModelMulti> temp = new ArrayList<>(buttonGroup.getSelections());
            temp.sort(Comparator.comparingInt(ButtonModelMulti::getComponentZOrder));
            //
            ArrayList<TaskInfo> res = new ArrayList<>(temp.size());
            for (ButtonModelMulti model : temp) res.add(((TaskItemView) model.getComponent()).getTask());
            return res;
        }

        /**
         * 切换多个任务的状态
         */
        private void switchTaskState(ArrayList<TaskInfo> tasks, boolean isStart) {
            doInBackground("操作", () -> {
                for (TaskInfo task : tasks) {
                    if (isStart && TaskState.PAUSE == task.getTaskState()) {
                        taskService.switchTaskState(task, true);
                    } else if (!isStart && task.isRunning()) {
                        taskService.switchTaskState(task, false);
                    }
                }
            });
        }

        private void menuSortAction(ActionEvent e) {
            String sortType = e.getActionCommand();
            log.info("排序: 下载中={}, {}", lastMenuDownloadTask, sortType);
            if (lastMenuDownloadTask) {
                taskService.getTaskList().getDownloading().sort(SortType.get(sortType).sorter);
                config.setDownloadSortType(sortType);
            } else {
                taskService.getTaskList().getFinished().sort(SortType.get(sortType).sorter);
                config.setFinishSortType(sortType);
            }
            SwingUtilities.invokeLater(() -> refreshTaskList(lastMenuDownloadTask));
            config.save();
        }

        private void menuAction(String command) {
            log.info("command={}", command);
            if (ALL_START.equals(command) || ALL_PAUSE.equals(command)) {
                switchTaskState(taskService.getTaskList().getDownloading(), ALL_START.equals(command));
                return;
            } else if (REFRESH.equals(command)) {
                taskService.reloadTaskFromFile();
                form.pTaskInfo.setTask(form.pTaskInfo.getTask());
                refreshTaskList();
                return;
            }
            ArrayList<TaskInfo> selectedTask = getSelectedTask();
            //直接执行的操作
            if (START.equals(command) || PAUSE.equals(command)) {
                if (selectedTask.size() == 1) onClickItem(selectedTask.get(0));
                switchTaskState(selectedTask, START.equals(command));
                return;
            }
            //需要确认框的操作
            StringBuilder sb = new StringBuilder("确认[" + command + "]?   ");
            for (TaskInfo task : selectedTask) sb.append("\n  ").append(task.getShortName());
            DialogHelper.yesNo(sb.toString()).okAction(() -> {
                /*
                可能的bug??: 删除任务时,ui还没更新,用户点击了[开始下载]
                性能问题
                    每次for都会重构listView所有元素,如果[要删除的,已有的]数据量大则boom
                        实测:几十个task,刷新100次,耗时2秒内,可以接受
                        不优化
                 */
                doInBackground("操作", () -> {
                    for (TaskInfo task : selectedTask) {
                        switch (command) {
                            case RESET:
                                taskService.resetTask(task);
                                break;
                            case DEL_TASK:
                                taskService.deleteTask(task, false);
                                break;
                            case DEL_TASK_N_FILE:
                                taskService.deleteTask(task, true);
                                break;
                        }
                    }
                });
            }).show(MainFrame.this);
        }
    }

}
